<template>
  <div>
    <div style="display: block">
      <el-switch

        v-model="isDraggable"
        active-color="#13ce66"
        active-text="启用拖拽"
        inactive-text="禁用拖拽">
      </el-switch>
<!--      当可拖拽的时候，显示批量提交按钮-->
      <el-button @click="updateBatchCategory" v-if="isDraggable">批量提交</el-button>
      <el-button @click="deleteBatchCategory" type="danger">批量删除</el-button>
    </div>
    <el-tree
      :data="treeNode"
      :props="defaultProps"
      node-key="catId"
      @node-drag-start="handleDragStart"
      @node-drag-enter="handleDragEnter"
      @node-drag-leave="handleDragLeave"
      @node-drag-over="handleDragOver"
      @node-drag-end="handleDragEnd"
      @node-drop="handleDrop"
      :allow-drag="allowDrag"
      show-checkbox
      :default-expand-all="false"
      :render-after-expand="true"
      @check-change="handleCheckChange"
      :expand-on-click-node="false"
      :draggable="isDraggable"
      :allow-drop="allowDrop"
      :default-expanded-keys="expandedKeys"
      ref="categoryTree"
    >
      <!--
      因为点击 append 或者 delete 都会触发 展开/收缩，所以要关闭 expand-on-click-node
      expand-on-click-node:是否在点击节点的时候展开或者收缩节点， 默认值为 true，如果为 false，则只有点箭头图标的时候才会展开或者收缩节点。
          :default-expand-all="false" 属性必须设置为false，否则浏览器异常卡顿

      draggable: 开启拖拽功能
      allow-drop: 拖拽时判定目标节点能否被放置。type 参数有三种情况：'prev'、'inner' 和 'next'，分别表示放置在目标节点前、插入至目标节点和放置在目标节点后
                  Function(draggingNode, dropNode, type)
      -->
      <span class="custom-tree-node" slot-scope="{ node, data }">
        <span>{{ node.label }}</span>
        <span>
          <el-button
            v-if="node.level<=2"
            type="text"
            size="mini"
            @click="() => append(data,node)">
            Append
          </el-button>

          <el-button
            v-if="node.childNodes.length==0"
            type="text"
            size="mini"
            @click="() => remove(node, data)">
            Delete
          </el-button>

          <el-button
            type="text"
            size="mini"
            @click="edit(node, data)">
            修改
          </el-button>
        </span>
      </span>
    </el-tree>
    <div>
      <!--
               close-on-click-modal	是否可以通过点击 modal（非对话框元素） 关闭 Dialog

      -->
      <el-dialog :title="category.isUpdate?'修改分类':'添加分类'" :visible.sync="dialogFormVisible"
                 :close-on-click-modal="false">
        <el-form :model="category">
          <el-form-item label="所属父节点" :label-width="formLabelWidth">
            <el-input v-model="category.parentName" autocomplete="off" disabled></el-input>
          </el-form-item>
          <el-form-item label="目录级别" :label-width="formLabelWidth">
            <el-input-number v-model="category.catLevel" :min="1" :max="3" label="第几级" disabled></el-input-number>
          </el-form-item>
          <el-form-item label="商品名称" :label-width="formLabelWidth">
            <el-input v-model="category.name" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="排序" :label-width="formLabelWidth">
            <el-input-number v-model="category.sort" :min="0" label="序号越低，优先级越高"></el-input-number>
          </el-form-item>
          <el-form-item label="计量单位" :label-width="formLabelWidth">
            <el-input v-model="category.productUnit" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="商品数量" :label-width="formLabelWidth">
            <el-input-number v-model="category.productCount" :min="0" label="数量"></el-input-number>
          </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button @click="dialogFormVisible = false">取 消</el-button>
          <el-button type="primary" @click="category.isUpdate?update():addCategory()">确 定</el-button>
        </div>
      </el-dialog>
    </div>
  </div>
</template>

<script>
import categoryApi from '../../../api/categoryApi'
  export default {
    name: 'category',
    data () {
      return {
        treeNode: [],
        defaultProps: {
          label: 'name',
          children: 'children'
        },
        expandedKeys: [],
        expandedKeysTemp: new Array(),
        dialogFormVisible: false,
        formLabelWidth: '120px',
        category: {
          parentCid: '',
          parentName: '',
          name: '',
          catLevel: 1,
          sort: 0,
          productUnit: '个',
          productCount: 100,
          isUpdate: false,
        },
        maxDepth: 0,
        updateCategory: new Array(),

        isDraggable:false
      }
    },
    methods: {
      getProductCategory () {
        categoryApi.getCategoryTree().then(({data}) => {
          this.treeNode = data.treeCategoryList.children
          console.log(this.treeNode)
        })
      },
      handleDragStart (node, ev) {
        //console.log('drag start', node)
      },
      handleDragEnter (draggingNode, dropNode, ev) {
        //console.log('tree drag enter: ', dropNode.label)
      },
      handleDragLeave (draggingNode, dropNode, ev) {
        //console.log('tree drag leave: ', dropNode.label)
      },
      handleDragOver (draggingNode, dropNode, ev) {
        //console.log('tree drag over: ', dropNode.label)
      },
      // 拖拽结束时（可能未成功）触发的事件，因为不一定成功，所以不使用这个方法
      handleDragEnd (draggingNode, dropNode, dropType, ev) {
        //console.log('tree drag end: ', draggingNode.data.name, dropNode.data.name, dropType)
      },

      /**
       * 拖拽成功的时候才会执行
       * 共四个参数，依次为：被拖拽节点对应的 Node、结束拖拽时最后进入的节点、被拖拽节点的放置位置（before、after、inner）、event
       */
      handleDrop (draggingNode, dropNode, dropType, ev) {
        console.log(`tree drop:移动的节点是${draggingNode.data.name}`, draggingNode, `与移动节点最后一次想连的节点是${dropNode.data.name}`, dropNode, dropNode.label, dropType)

        //1. 获取当前节点的最新父节点id
        let parentCid = null;

        let level = null;

        // 父节点就是插入节点的父节点
        parentCid = dropNode.data.parentCid;
        // 级别就是插入节点的级别
        //level = dropNode.data.catLevel;
        level = dropNode.level;

        // 在目标节点的前后插入
        if (dropType === 'before' || dropType === 'after') {

          // 判断是不是顶级节点
          if (dropNode.data.parentCid === 0) {
            // 在顶级节点的前后插入

            // 判断并更新子节点的层级
            this.updateChildrenNodeCatLevel(draggingNode, dropNode.level + 1)

            // 那么遍历目标节点的父节点索引来进行排序
            dropNode.parent.data.forEach((node, index) => {

              console.log('顶级节点前后插入', node, node.name, index, level, parentCid)
              this.updateSortCatLevelParentCid(node, index, level, parentCid)

            })
            //console.log("插入的节点的级别",dropNode.level)

          } else {

            // 判断并更新子节点的层级
            this.updateChildrenNodeCatLevel(draggingNode, dropNode.level + 1)

            // 不是顶级节点，且在前后插入

            dropNode.parent.data.children.forEach((node, index) => {

              console.log('不是顶级节点前后插入', node, node.name, index, level, parentCid)
              this.updateSortCatLevelParentCid(node, index, level, parentCid)

            })
          }

        } else {
          // 以下是插入到节点的内部
          level = dropNode.level + 1

          // 判断并更新子节点的层级
          this.updateChildrenNodeCatLevel(draggingNode, dropNode.level + 2)
          //console.log('这次移动的是顶级节点，直接去掉children，进行遍历根节点', dropNode.parent.data)

          if (dropNode.data.children) {
            dropNode.data.children.forEach((node, index) => {

              // 插入节点的父id就是被插入节点的id，因为是内部插入
              parentCid = dropNode.data.catId
              level = dropNode.data.catLevel + 1
              console.log('插入到内部', node, node.name, index, level, parentCid)
              this.updateSortCatLevelParentCid(node, index, level, parentCid)

            })
          }
        }

        if(this.isDraggable){

          //this.updateBatchApi();
        }
        console.log('展开的节点是', parentCid)
        this.expandedKeysTemp.push(parentCid);
        this.maxDepth = 0;
      },

      updateSortCatLevelParentCid (node, index, catLevel, parentCid) {

        //node.sort = index 害人不浅
        if (index == node.sort && catLevel == node.catLevel && parentCid == node.parentCid) {
          console.log('都一样，不用更新了', node.sort, node.catLevel, node.parentCid)
          return
        }

        this.updateCategory.push(
          {
            catId: node.catId,
            sort: index == node.sort ? null : index,
            //3. 当前拖拽节点的最新层级
            catLevel: catLevel == node.catLevel ? null : catLevel,
            parentCid: parentCid == node.parentCid ? null : parentCid
          }
        )

        /*this.updateApi({
          catId: node.catId,
          sort: index == node.sort ? null : index,
          //3. 当前拖拽节点的最新层级
          catLevel: catLevel == node.catLevel ? null : catLevel,
          parentCid: parentCid == node.parentCid ? null : parentCid
        })*/
        console.log('添加后当前数组中的数据是', this.updateCategory)

        // 更新其他节点的顺序
        //console.log(`已更新${node.name}的顺序为${index}，${catLevel == node.catLevel ? '级别不做变更' : '级别已经更新为' + node.catLevel}`)
      },

      updateChildrenNodeCatLevel (draggingNode, level) {
        //console.log('修改子节点层级接收到的参数是', draggingNode, level)
        if (draggingNode.data && draggingNode.data.children && draggingNode.data.children.length > 0) {

          draggingNode.data.children.forEach(n => {

            if (level != n.catLevel) {
              this.updateCategory.push(
                {
                  catId: n.catId,
                  catLevel: level
                }
              )

              /*this.updateApi({
                catId: n.catId,
                catLevel: level
              })*/
              console.log(`修改子节点${n.name} ${n.catId} 的层级为${level}`)
              this.updateChildrenNodeCatLevel(n, level + 1)
            }

          })

        }
      },

      updateBatchApi (category) {
        //2. 当前拖拽节点的最新排序
        this.$http({
          url: this.$http.adornUrl('/pms/category/updateBatch'),
          method: 'post',
          data: this.$http.adornData(this.updateCategory, false) // 这里使用false是为了不能添加时间戳到数组中
        }).then(({data}) => {
            this.getProductCategory()

            if (data && data.code === 0) {
              this.$message({
                message: `已更新`,
                type: 'success',
                //duration: 1500,
              })
            } else {
              this.$message.error(data.msg)
            }
          }
        ).catch(() => {
          this.getProductCategory()
        })
        this.updateCategory = [];
        this.expandedKeys = this.expandedKeysTemp;
        this.expandedKeysTemp = [];
      },

      allowDrop (draggingNode, dropNode, type) {
        //console.log('是否允许放置', draggingNode, dropNode, type)
        /**
         * 被拖动的当前节点及其所在的父节点的总层数不能大于3
         * */

        // 获取当前被拖动节点的最大深度
        this.maxDepth = 0
        this.calculateMaxDepth(draggingNode)

        // 当前正在拖动的节点的最深层 + 拖动到目标节点父节点所在的深度不大于3即可

        // 当前节点的最大深度 - 当前节点的深度 + 1 = 拖拽后当前节点的实际深度 （因为没有被拖拽的部分，不能计入实际深度）
        //let currentNodeDepth = this.maxDepth - draggingNode.data.catLevel + 1 // 多次拖拽之后因为没有同步上一次更新后的数据库，所以会有误
        let currentNodeDepth = this.maxDepth - draggingNode.level + 1 // 使用节点层级本身的级别

        //console.log('被拖拽部分的实际层数', currentNodeDepth)

        //console.log('拖拽后的总层数', currentNodeDepth + dropNode.level /*dropNode.data.catLevel*/)

        // 拖拽后，当前拖拽过来的实际层数 + 目标节点的层数不能 >3，即可拖拽
        // 因为是拖拽到内部，那么目的节点的已有层数就是内部节点的父节点，相当于是目的节点本身，所以总层数 <= 3 即可
        if (type == 'inner' && currentNodeDepth + dropNode.level <= 3) {
          return true
        }

        // 拖拽到目的节点的前或者后，目的节点实际有效的层数是目的节点的父节点层数。然后目的节点的父节点层数加上拖拽过来的节点的有效层数 <=3 即可
        // 这里不是使用目的节点的父节点，而是使用了目的节点本身，所以不是 <=3 而是 <=4
        if (currentNodeDepth + dropNode.level <= 4) {
          return true
        }

        // 这种写法与上一种写法等效
        if (currentNodeDepth + dropNode.parent.level <= 3) {
          return true
        }

        return false
      },
      allowDrag (draggingNode) {
        // return draggingNode.data.label.indexOf('三级 3-2-2') === -1
        //console.log('是否允许拖拽', draggingNode)
        return true
      },
      /**
       * 计算节点的最大深度
       * */
      calculateMaxDepth (node) {
        if (node.level > this.maxDepth) {
          this.maxDepth = node.level
          if (node.childNodes && node.childNodes.length > 0) {
            node.childNodes.forEach(n => {
              this.calculateMaxDepth(n)
            })
          }
        }
      },
      append (data, node) {
        this.initializeCategory()

        console.log(data)
        this.category.parentCid = data.catId
        this.category.parentName = data.name
        this.category.catLevel = data.catLevel + 1
        this.dialogFormVisible = true
        this.expandedKeys[0] = node.data.catId
      },

      remove (node, data) {
        console.log(node, data)
        /*const parent = node.parent;
        const children = parent.data.children || parent.data;
        const index = children.findIndex(d => d.id === data.id);
        children.splice(index, 1);*/

        this.$confirm(`此操作将永久删除${data.name}, 是否继续?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          let catIdArray = [data.catId]
          this.deleteProductCategory(catIdArray)
          // this.expandedKeys[0] = node.parent.id; 这个不一定是父节点的id
          this.expandedKeys[0] = node.parent.data.catId
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          })
        })

      },

      edit (node, data) {

        /*
        这样直接取浏览器缓存的值可能会导致其他人先修改，而这里用的是以前的数据，导致数据不一致
        所以使用直接从数据库中获取最新值
        但是可以先用来临时显示，临时显示还能临时同步值
        */
        this.category = {
          parentCid: data.parentCid,
          parentName: node.parent.data.name,
          name: data.name,
          catLevel: data.catLevel,
          sort: data.sort,
          productUnit: data.productUnit,
          productCount: data.productCount,
          catId: data.catId,
          isUpdate: true
        }
        // 先显示，后查询，不会有卡断感
        this.dialogFormVisible = true

        this.$http({
          url: this.$http.adornUrl(`/pms/category/info/${data.catId}`),
          method: 'get',
        }).then(({data}) => {
          /*
          这样太麻烦
          this.category = {
            parentCid: data.category.parentCid,
            parentName: node.parent.data.name,// 父节点名字保持不变，其余都重新从数据库中获取
            name: data.category.name,
            catLevel: data.category.catLevel,
            sort: data.category.sort,
            productUnit: data.category.productUnit,
            productCount: data.category.productCount,
            catId: data.category.catId,
            isUpdate: true
          }*/
          this.category = data.category
          this.category.parentName = node.parent.data.name// 父节点名字保持不变，其余都重新从数据库中获取
          this.category.isUpdate = true //忘了标识位就成了插入操作了

          console.log('数据库中的该商品是', data.category)
          console.log('节点信息是', node)

        })

      },
      update () {
        /**
         * 我们不希望更新全部目录，而是只更新部分数据
         * 使用揭构表达式
         * */
        let {catId, sort, productUnit, productCount, name} = this.category

        /*      这样写太麻烦，可以优化写法
                let tempCategory = {
                  catId:catId,
                  sort:sort,
                  productUnit:productUnit,
                  productCount:productCount,
                  name:name
                };*/

        let tempCategory = {
          catId,
          sort,
          productUnit,
          productCount,
          name
        }

        this.$http({
          url: this.$http.adornUrl('/pms/category/update'),
          method: 'post',
          data: this.$http.adornData(tempCategory, false)
        }).then(({data}) => {
          if (data && data.code === 0) {

            this.expandedKeys[0] = this.category.catId

            this.dialogFormVisible = false
            this.getProductCategory()
            this.$message({
              message: this.category.name + ' 已更新',
              type: 'success',
              duration: 1500,
              onClose: () => {

              }
            })
            this.initializeCategory()

          } else {
            this.$message.error(data.msg)
          }
        }).catch(() => {
        })
      },

      handleCheckChange (data, checked, indeterminate) {
        console.log(data, checked, indeterminate)
      },
      /**
       * 根据ids删除cateGory
       * */
      deleteProductCategory (catIdArray) {


        this.$http({
          url: this.$http.adornUrl('/pms/category/removeByCategoryIds/'),
          method: 'post',
          data: this.$http.adornData(catIdArray, false)
        }).then(({data}) => {
          if (data && data.code === 0) {
            this.getProductCategory()
            this.$message({
              message: catIdArray + ' 已被删除',
              type: 'success',
              duration: 1500,
              onClose: () => {
                //this.getProductCategory()
              }
            })
          } else {
            this.$message.error(data.msg)
          }
        }).catch(() => {
        })
      },
      addCategory () {

        console.log('要添加的category数据', this.category)

        this.$http({
          url: this.$http.adornUrl('/pms/category/save'),
          method: 'post',
          data: this.$http.adornData(this.category)
        }).then(({data}) => {
          if (data && data.code === 0) {
            this.initializeCategory()
            this.getProductCategory()
            this.$message({
              message: `${this.category.name}添加成功`,
              type: 'success',
              duration: 1500,
              onClose: () => {

              }
            })
          } else {
            this.$message.error(data.msg)
          }
        }).catch(() => {
        })

        this.dialogFormVisible = false
      },
      initializeCategory () {
        /**
         * 数据初始化
         * */
        this.category = {
          parentCid: '',
          parentName: '',
          name: '',
          catLevel: 1,
          sort: 0,
          productUnit: '个',
          productCount: 100,
          isUpdate: false
        }
      },
      draggable () {
        this.isDraggable = !this.isDraggable
      },
      updateBatchCategory(){

        this.updateBatchApi();
      },

      deleteBatchCategory(){
        /**
         * 若节点可被选择（即 show-checkbox 为 true），则返回目前被选中的节点所组成的数组
         * (leafOnly, includeHalfChecked) 接收两个 boolean 类型的参数，1. 是否只是叶子节点，默认值为 false 2. 是否包含半选节点，默认值为 false
         * */
        let selectedNodeArray =  this.$refs.categoryTree.getCheckedNodes(false,false);

        let selectedNodeNameArray = new Array();

        selectedNodeArray.forEach(n=>{
          selectedNodeNameArray.push(n.name);
        })


        this.$confirm(`此操作将永久删除【${selectedNodeNameArray}】, 是否继续?`, '提示', {
          confirmButtonText: '删除',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {


          //console.log("获取到的已选择节点是",selectedNodeArray);

          let selectedNodeCatIdArray = new Array();

          selectedNodeArray.forEach((n,index)=>{
            console.log("索引",index,n);
            selectedNodeCatIdArray.push(n.catId);
          })


          this.$http({
            url: this.$http.adornUrl(`/pms/category/deleteBatch`),
            method: 'put',
            data: this.$http.adornData(selectedNodeCatIdArray, false)
          }).then(({data}) => {
            if (data && data.code === 0) {
              this.getProductCategory()
              this.$message({
                message: `删除成功`,
                type: 'success',
                duration: 1500,
                onClose: () => {

                }
              })

            } else {
              this.$message.error(data.msg)
            }
          }).catch(() => {
          })

        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });
        });


      },


    }
    ,
    created () {
      this.getProductCategory()
    }
    ,
    computed: {}
  }
</script>

<style scoped>

</style>
